package com.hw.common.utils;


import cn.hutool.core.util.StrUtil;
import com.hw.common.enums.TimePartitionEnum;
import com.hw.common.exception.BaseException;
import org.joda.time.DateTimeZone;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Clock;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DateUtils {
    public static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd";
    public static final String DEFAULT_MONTH_PATTERN = "yyyy-MM";
    public static final String DATE_PATTERN_8 = "yyyyMMdd";
    public static final String DATE_PATTERN_MM_DD = "MM-dd";
    public static final String DEFAULT_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    public static final String DATETIME_PATTERN = "yyyy年MM月dd日";
    private static final String YEAR_START_SUFFIX = "-01-01 00:00:00";
    private static final String YEAR_END_SUFFIX = "-12-31 23:59:59";
    public static final String TIME_BEGIN_SUFFIX = " 00:00:00";
    public static final String TIME_END_SUFFIX = " 23:59:59";
    private static final int TIME_INITIAL_VALUE = 0;
    private static final String DATE_FORMAT_EXPRESSION = "(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)$";
    private static final String DATETIME_FORMAT_EXPRESSION = "((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)) ([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$";

    public DateUtils() {

    }

    public static String parseStringDate(String dateString) throws BaseException {
        if (StrUtil.isBlank(dateString)) {
            return null;
        } else {
            Date date = parseDateTime(dateString);
            return (new SimpleDateFormat("yyyy年MM月dd日")).format(date);
        }
    }

    public static Date parseDateTime(String dateString, String parsePattern) throws BaseException {
        SimpleDateFormat format = new SimpleDateFormat(parsePattern);

        try {
            return format.parse(dateString);
        } catch (ParseException var4) {
            throw new BaseException("Formatting fail");
        }
    }

    public static Date parseDate(String dateString) throws BaseException {
        return parseDateTime(dateString, "yyyy-MM-dd");
    }

    public static Date parseDateTime(String dateString) throws BaseException {
        return parseDateTime(dateString, "yyyy-MM-dd HH:mm:ss");
    }

    public static String formatDateTime(Date date, String pattern) {
        DateFormat dateFormat = new SimpleDateFormat(pattern);
        return dateFormat.format(date);
    }

    public static String formatDate(Date date) {
        return formatDateTime(date, "yyyy-MM-dd");
    }

    public static String formatDateTime(Date date) {
        return formatDateTime(date, "yyyy-MM-dd HH:mm:ss");
    }

    public static Date getSysDate() {
        return new Date();
    }

    public static String getBeforeDate(Date dateTime, int day) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        Calendar c = Calendar.getInstance();
        c.setTime(dateTime);
        c.add(5, -day);
        return format.format(c.getTime());
    }

    public static Date getBeforeMinute(Date dateTime, int minutes) {
        new SimpleDateFormat("yyyy-MM-dd");
        Calendar c = Calendar.getInstance();
        c.setTime(dateTime);
        c.add(12, -minutes);
        return c.getTime();
    }

    public static String getAfterDate(Date dateTime, int day) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        Calendar c = Calendar.getInstance();
        c.setTime(dateTime);
        c.add(5, day);
        return format.format(c.getTime());
    }

    public static Date getYearStartTime(int year) throws BaseException {
        String startTime = year + "-01-01 00:00:00";
        return parseDateTime(startTime, "yyyy-MM-dd HH:mm:ss");
    }

    public static Date getYearEndTime(int year) throws BaseException {
        String endTime = year + "-12-31 23:59:59";
        return parseDateTime(endTime, "yyyy-MM-dd HH:mm:ss");
    }

    public static int getTimeDifference(Date startTime, Date endTime) {
        long DAY_MILLISECOND = 86400000L;
        Calendar cal = Calendar.getInstance();
        cal.setTime(startTime);
        long start = cal.getTimeInMillis();
        cal.setTime(endTime);
        long end = cal.getTimeInMillis();
        long between_days = (end - start) / 86400000L;
        return (int)between_days;
    }

    public static int getTimeDifference(String startDate, String endDate, String format) throws BaseException {
        Date startTime = parseDateTime(startDate, format);
        Date endTime = parseDateTime(endDate, format);
        return getTimeDifference(startTime, endTime);
    }

    public static Date getDayStartTime(Date dayTime) throws BaseException {
        if (null == dayTime) {
            throw new BaseException("Parameter null");
        } else {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(dayTime);
            calendar.set(11, 0);
            calendar.set(12, 0);
            calendar.set(13, 0);
            calendar.set(14, 0);
            Date dayStartTime = calendar.getTime();
            return dayStartTime;
        }
    }

    public static int compare(Date date1, Date date2) throws BaseException {
        if (date1 == null && date2 == null) {
            throw new BaseException("参数为空");
        } else if (date1 == null) {
            return -1;
        } else if (date2 == null) {
            return 1;
        } else if (date1.equals(date2)) {
            return 0;
        } else {
            return date1.before(date2) ? -1 : 1;
        }
    }

    public static int getDaysInYear(LocalDate date) {
        if (null == date) {
            return 0;
        } else {
            int yearDays = date.get(ChronoField.DAY_OF_YEAR);
            return yearDays;
        }
    }

    public static List<String> getTimePartitions(Date start, Date end, TimePartitionEnum timePartition) throws BaseException {
        if (start != null && end != null && timePartition != null) {
            if (start.after(end)) {
                throw new BaseException("参数为空");
            } else {
                Calendar startCal = Calendar.getInstance();
                startCal.setTime(start);
                Calendar endCal = Calendar.getInstance();
                endCal.setTime(end);
                String pattern = timePartition.getPattern();
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
                ArrayList timePartitionList = new ArrayList();

                while(!startCal.after(endCal)) {
                    timePartitionList.add(simpleDateFormat.format(startCal.getTime()));
                    startCal.add(timePartition.getCalendarConst(), BigDecimal.ONE.intValue());
                }

                return timePartitionList;
            }
        } else {
            throw new BaseException("参数为空");
        }
    }

    public static int calculateRU(Date date) {
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        int dayOfYear = instance.get(6);
        int year = instance.get(1) * 1000;
        return year + dayOfYear;
    }

    public static String CalculateDate(String YearAndDay) {
        Calendar instance = Calendar.getInstance();
        String year = YearAndDay.substring(0, 4);
        String days = YearAndDay.substring(4);
        instance.set(1, Integer.valueOf(year));
        instance.set(6, Integer.valueOf(days));
        Date time = instance.getTime();
        return formatDate(time);
    }

    public static boolean isDate(String date) {
        Pattern pat = Pattern.compile("(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)$");
        Matcher mat = pat.matcher(date);
        boolean dateType = mat.matches();
        return dateType;
    }

    public static boolean isDateTime(String date) {
        Pattern pat = Pattern.compile("((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)) ([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$");
        Matcher mat = pat.matcher(date);
        boolean dateType = mat.matches();
        return dateType;
    }

    public static String getTimestampStr(Timestamp timestamp, String format) {
        if (format == null) {
            format = "yyyy-MM-dd HH:mm:ss";
        }

        SimpleDateFormat sdf = new SimpleDateFormat(format);
        String dateStr = sdf.format(timestamp);
        return dateStr;
    }

    public static String getCurrentTimestampStr(String format) {
        if (format == null) {
            format = "yyyy-MM-dd HH:mm:ss";
        }

        Timestamp currentDate = new Timestamp(System.currentTimeMillis());
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        String dateStr = sdf.format(currentDate);
        return dateStr;
    }

    public static Timestamp getCurrentTimestampYear() {
        Calendar c = Calendar.getInstance();
        c.set(2, 0);
        c.set(5, 1);
        c.set(11, 0);
        c.set(12, 0);
        c.set(13, 0);
        return new Timestamp(c.getTimeInMillis());
    }

    public static Timestamp getYearBeginTime(long millis) {
        Calendar c = Calendar.getInstance();
        c.setTimeInMillis(millis);
        c.set(2, 0);
        c.set(5, 1);
        c.set(11, 0);
        c.set(12, 0);
        c.set(13, 0);
        c.set(14, 0);
        return new Timestamp(c.getTimeInMillis());
    }

    public static Timestamp getYearEndTime(long millis) {
        Calendar c = Calendar.getInstance();
        c.setTimeInMillis(millis);
        c.set(2, 11);
        c.set(5, c.getActualMaximum(5));
        c.set(11, 23);
        c.set(12, 59);
        c.set(13, 59);
        c.set(14, 999);
        return new Timestamp(c.getTimeInMillis());
    }

    public static Timestamp getMonthBeginTime(long millis) {
        Calendar c = Calendar.getInstance();
        c.setTimeInMillis(millis);
        c.set(5, 1);
        c.set(11, 0);
        c.set(12, 0);
        c.set(13, 0);
        return new Timestamp(c.getTimeInMillis());
    }

    public static Timestamp getMonthBeforeMonth(long millis, int sub) {
        Calendar c = Calendar.getInstance();
        c.setTimeInMillis(millis);
        c.add(2, sub);
        return new Timestamp(c.getTimeInMillis());
    }

    public static Timestamp getMonthEndTime(long millis) {
        Calendar c = Calendar.getInstance();
        c.setTimeInMillis(millis);
        c.set(5, c.getActualMaximum(5));
        c.set(11, 23);
        c.set(12, 59);
        c.set(13, 59);
        return new Timestamp(c.getTimeInMillis());
    }

    public static Timestamp getCurrentPastTimestamp(int past, int calendarType) {
        Long times = System.currentTimeMillis();
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(times);
        calendar.set(calendarType, calendar.get(calendarType) - past);
        return new Timestamp(calendar.getTimeInMillis());
    }

    public static Timestamp getPastTimestamp(Long times, int past, int calendarType) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(times);
        calendar.set(calendarType, calendar.get(calendarType) - past);
        calendar.set(11, 0);
        calendar.set(12, 0);
        calendar.set(13, 0);
        return new Timestamp(calendar.getTimeInMillis());
    }

    public static Timestamp getPastTimestampBegin(int past) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(6, calendar.get(6) - past);
        calendar.set(11, 0);
        calendar.set(12, 0);
        calendar.set(13, 0);
        calendar.set(14, 0);
        return new Timestamp(calendar.getTimeInMillis());
    }

    public static Timestamp getPastTimestampBegin(long millis, int past) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(millis);
        calendar.set(6, calendar.get(6) - past);
        calendar.set(11, 0);
        calendar.set(12, 0);
        calendar.set(13, 0);
        calendar.set(14, 0);
        return new Timestamp(calendar.getTimeInMillis());
    }

    public static Timestamp getCurrentTimestampBegin() {
        return new Timestamp(getCurrentLongBegin());
    }

    public static long getCurrentLongBegin() {
        Calendar c = Calendar.getInstance();
        c.setTime(new Date());
        c.set(11, 0);
        c.set(12, 0);
        c.set(13, 0);
        c.set(14, 0);
        return c.getTimeInMillis();
    }

    public static Timestamp getCurrentTimestampEnd() {
        return new Timestamp(getCurrentLongEnd());
    }

    public static long getCurrentLongEnd() {
        Calendar c = Calendar.getInstance();
        c.setTime(new Date());
        c.set(11, 23);
        c.set(12, 59);
        c.set(13, 59);
        return c.getTimeInMillis();
    }

    public static long getTimeLongEnd(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(11, 23);
        c.set(12, 59);
        c.set(13, 59);
        return c.getTimeInMillis();
    }

    public static Timestamp getStringTimestamp(String dateStr, String format) {
        if (StrUtil.isBlank(dateStr)) {
            return null;
        } else {
            if (StrUtil.isBlank(format)) {
                format = "yyyy-MM-dd HH:mm:ss";
            }

            if (dateStr.length() == 10) {
                format = "yyyy-MM-dd";
            } else if (dateStr.length() == 16) {
                format = "yyyy-MM";
            } else if (dateStr.length() == 19) {
                format = "yyyy-MM-dd HH:mm:ss";
            }

            SimpleDateFormat sdf = new SimpleDateFormat(format);
            Date date = null;

            try {
                date = sdf.parse(dateStr);
            } catch (Exception var5) {
                var5.printStackTrace();
            }

            Timestamp ts = new Timestamp(date.getTime());
            return ts;
        }
    }

    public static String getAge(Date birthday,Date date){
        if( null == birthday ||  null == date ){
            return "";
        }

        LocalDate currentDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        LocalDate birthDate = birthday.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

        Period period = Period.between(currentDate,birthDate );
        int age = period.getYears();
        int months = period.getMonths();
        int days = period.getDays();

        return age + " ys " + months + " mos " + days + " days ";
    }

    public static Date convertToLocal(Date date) {
        if( null == date ){
            return null;
        }
        LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        ZoneId fromZone = ZoneId.of("UTC");
        ZoneId toZone = ZoneId.of("Asia/Tokyo");

        ZonedDateTime fromZonedDateTime = ZonedDateTime.of(localDateTime, fromZone);
        ZonedDateTime toZonedDateTime = fromZonedDateTime.withZoneSameInstant(toZone);
        LocalDateTime newDatetime = toZonedDateTime.toLocalDateTime().minusSeconds(1L);

        return Date.from(newDatetime.atZone(ZoneId.systemDefault()).toInstant());
    }
}